/*
 * Copyright (c) 2025 CAX Conversion Project
 */
package com.comac.ins.common.core.aspectj;


import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import cn.hutool.extra.spring.SpringUtil;
import com.comac.ins.common.core.annotations.BizEventListener;
import com.comac.ins.common.core.domain.model.BizEvent;
import com.comac.ins.common.core.enums.BizEventTypeEnum;
import lombok.extern.slf4j.Slf4j;

import static com.comac.ins.common.core.constant.Constants.TRACE_ID;


/**
 * 业务事件监听拦截切面
 *
 * @author zhanghongyi
 * @date 2024/7/5 15:32
 * @description BizEventListenerAspect
 */
@Aspect
@Component
@Slf4j
public class BizEventListenerAspect {
    /**
     * 对加了注解的方法进行拦截
     */
    @Pointcut("@annotation(com.comac.ins.common.core.annotations.BizEventListener)")
    public void pointCut() {
    }

    /**
     * 业务事件监听切面
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String oldTraceId = MDC.get(TRACE_ID);
        boolean eventAsync = true;
        BizEventTypeEnum eventType = null;
        Object result = null;
        try {
            Object[] paramValues = proceedingJoinPoint.getArgs();
            for (Object param : paramValues) {
                if (param instanceof BizEvent<?>) {
                    String traceId = ((BizEvent<?>) param).getTraceId();
                    eventAsync = ((BizEvent<?>) param).isAsync();
                    eventType = ((BizEvent<?>) param).getEventType();
                    if (StringUtils.isNotBlank(traceId)) {
                        MDC.put(TRACE_ID, traceId);
                    }
                    break;
                }
            }
            MethodSignature methodSign = (MethodSignature) proceedingJoinPoint.getSignature();
            Method method = methodSign.getMethod();
            BizEventListener annotation = method.getAnnotation(BizEventListener.class);
            if (annotation != null) {
                result = doProcess(proceedingJoinPoint, annotation, eventType, eventAsync);
            }
        } finally {
            if (StringUtils.isBlank(oldTraceId)) {
                MDC.remove(TRACE_ID);
            } else {
                MDC.put(TRACE_ID, oldTraceId);
            }
        }
        return result;
    }

    /**
     * 异步调用拦截的方法
     */
    @Async("bizEventListenerAsyncExecutor")
    public void callListenFunction(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        proceedingJoinPoint.proceed();
    }

    private Object doProcess(ProceedingJoinPoint proceedingJoinPoint, BizEventListener annotation, BizEventTypeEnum eventType, boolean eventAsync) throws Throwable {
        Object result = null;
        BizEventTypeEnum[] listenedEventTypes = BizEventTypeEnum.values();
        if (annotation.listenedBizEvent().length > 0) {
            listenedEventTypes = annotation.listenedBizEvent();
        }
        if ((new HashSet<>(Arrays.asList(listenedEventTypes))).contains(eventType)) {
            //事件命中监听的业务事件类型才进行调用，否则直接返回
            if (annotation.async() && eventAsync) {
                //默认使用异步方法，如果注解上或者事件上标记了不使用异步则进行同步调用
                //类内调用异步方法必须经过代理类，否则异步注解不生效
                SpringUtil.getBean(BizEventListenerAspect.class).callListenFunction(proceedingJoinPoint);
            } else {
                result = proceedingJoinPoint.proceed();
            }
        }
        return result;
    }
}
